
//#include <linux/printk.h> //该头文件位于linux内核的源码

#include <linux/module.h> //module_init 
#include <linux/printk.h>
#include <linux/cdev.h> //cdev
#include <linux/fs.h> //file_operations
#include <linux/device.h> //class_create
#include <linux/err.h> // -EBUSY
#include <linux/io.h> //ioremap

#include <linux/uaccess.h> //copy_from_user

#include <linux/gpio.h> //gpio

#include <cfg_type.h> //关于硬件平台GPIO定义
#include <linux/miscdevice.h> //misc
//定义Led的结构体，包括管脚和名字
struct gec6818_ptc_gpio {
	int gpio;
	char *name;	
};

struct gec6818_ptc_gpio ptc_gpio = {PAD_GPIO_D+17,"GPIOD17"};
//struct gec6818_ptc_gpio ptc_gpio = {PAD_GPIO_D+21,"GPIOD21"};


/*
对于LED来讲，应用程序的接口：open,write,close
int (*open) (struct inode *, struct file *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

驱动程序接口函数的实现，必须要跟函数指针的类型保持一致
*/

int gec6818_ptc_open(struct inode *node, struct file *file)
{
	printk("gec6818_ptc_open\n");
	return 0;
}
/*
  驱动程序与应用程序之间的通信：传输数据
  应用程序通过write() ---->系统调用 --->驱动程序中的gec6818_led_write()
  数据的格式：ptc的状态 --- char
  
  驱动程序接受应用程序数据
  由于该数据来自于用户空间，驱动程序不能直接访问，必须使用copy_from_user  ---- write函数
  int copy_from_user(void *to, const void __user *from, int n)
   void __user *from  ---->用户空间的数据
   void *to ----->内核空间的数据
   
 驱动程序发送数据给应用程序  ----- read
    int copy_to_user(void __user *to, const void *from, int n)

*/
ssize_t gec6818_ptc_write(struct file *file, const char __user *user_buff, size_t size, loff_t *offset)
{
	int ret;
	//定义一个数据格式
	
	char kbuf;
	printk("gec6818_ptc_write\n");
	if(size != 1)
		return -EINVAL; //   Invalid argument

	//通过copy_from_user把应用层的数据存放在内核空间
	ret = copy_from_user(&kbuf,user_buff,size);
	if(ret != 0)
		return -EFAULT; //bad address

	
	 if(kbuf == 1){
		 //ptc ----> 叫
		 //*(unsigned int*)gpioc_va_out |= (1<<14); // GPIOCOUT
		 gpio_set_value(ptc_gpio.gpio, 1);
	 }else if(kbuf == 0){
		 //ptc ----> 不叫
		gpio_set_value(ptc_gpio.gpio, 0);
	 }else{
		return -EINVAL; //   Invalid argument
	 }
	
	
	return size;
}

int gec6818_ptc_release(struct inode *node, struct file *file)
{
	printk("gec6818_ptc_release\n");
	return 0;
}

//[2]
static struct file_operations gec6818_ptc_ops = {
	.owner = THIS_MODULE,
	.open = gec6818_ptc_open,
	.write = gec6818_ptc_write,
	.release = gec6818_ptc_release,//当应用程序调用close时，执行驱动程序中的接口函数
};
//[1] 定义一个混杂设备
static struct miscdevice ptc_misc = {
		.name = "gec6818_ptc_drv",  //设备文件的名字
		.fops = &gec6818_ptc_ops,
		.minor = MISC_DYNAMIC_MINOR, //设置次设备号，MISC_DYNAMIC_MINOR --- 表示由系统动态分配一个次设备号
};
static int __init gec6818ptc_init(void)
{
	int ret;
	printk("gec6818_ptc_init\n"); //相当于应用程序的printf，在驱动程序中，不支持libc
								  //所有的接口都来看于linux内核的接口:位于include/linux
	
	//初始化的工作
	gpio_free(ptc_gpio.gpio);
	 ret = gpio_request(ptc_gpio.gpio,ptc_gpio.name);
	  if(ret < 0)
	  {
		  printk("request  %s error\n",ptc_gpio.name);
		  ret = -EBUSY;
		  goto gpio_request_error;
		  
	  }  
	  //设置GPIO为输出模式
	  gpio_direction_output(ptc_gpio.gpio, 0);
	  
	  gpio_set_value(ptc_gpio.gpio, 0);
		  
	 //[4] 注册混杂设备
	  ret = misc_register(&ptc_misc);
	  if(ret < 0)
	  {
		  printk("misc_register error\n");
		  ret = -EBUSY;
		  goto misc_register_error;
	  }
		  
	return 0;
	misc_register_error:
	gpio_free(ptc_gpio.gpio); 
    gpio_request_error:
	 return ret;
	
}


static void __exit gec6818ptc_exit(void)
{
	printk("<4>" "gec6818ptc_exit\n");

	//[5] 注销混杂设备
	misc_deregister(&ptc_misc);
	gpio_free(ptc_gpio.gpio); 
	
}

module_init(gec6818ptc_init); //当用户执行insmod ptc_drv.ko时，调用module_init，然而，
								  //gec6818ptc_init被调用
module_exit(gec6818ptc_exit); //当用户执行rmmod ptc_drv时，调用moudle_exit，

MODULE_AUTHOR("1853.zhang3");
MODULE_DESCRIPTION("GEC6818 ptc Device Driver");
MODULE_LICENSE("GPL");